#ifndef CAMERA_H_
#define CAMERA_H_

#include <vmath/vmath.h>

namespace goatgfx {

class Camera {
protected:
	float roll;

	mutable struct {
		bool valid;
		Matrix4x4 mat;
	} mcache, mcache_inv;

	virtual void calc_matrix(Matrix4x4 *mat) const = 0;
	virtual void calc_inv_matrix(Matrix4x4 *mat) const;

	void inval_cache() { mcache.valid = mcache_inv.valid = false; }
	void set_glmat(const Matrix4x4 &m) const;

public:
	Camera();
	virtual ~Camera();

	const Matrix4x4 &matrix() const;
	const Matrix4x4 &inv_matrix() const;

	void use() const;
	void use_inverse() const;

	// these do nothing, override to provide input handling
	virtual void input_move(float x, float y, float z);
	virtual void input_rotate(float x, float y, float z);
	virtual void input_zoom(float factor);
};

class OrbitCamera : public Camera {
protected:
	float theta, phi, rad;

	void calc_matrix(Matrix4x4 *mat) const;
	void calc_inv_matrix(Matrix4x4 *mat) const;

public:
	OrbitCamera();
	virtual ~OrbitCamera();

	void input_rotate(float x, float y, float z);
	void input_zoom(float factor);
};

class FpsCamera : public OrbitCamera {
protected:
	Vector3 pos;

	void calc_matrix(Matrix4x4 *mat) const;
	void calc_inv_matrix(Matrix4x4 *mat) const;

public:
	void input_move(float x, float y, float z);

	const Vector3 &get_position() const;
};

class FlyCamera : public Camera {
private:
	Vector3 pos;
	Quaternion rot;

	void calc_matrix(Matrix4x4 *mat) const;
	//void calc_inv_matrix(Matrix4x4 *mat) const;

public:
	FlyCamera();

	const Vector3 &get_position() const;
	const Quaternion &get_rotation() const;

	void input_move(float x, float y, float z);
	void input_rotate(float x, float y, float z);
};


class VRFpsCamera : public FpsCamera {
private:
	float neck_eye_dist;
	float prev_angles[3];

	void calc_matrix(Matrix4x4 *mat) const;
	void calc_inv_matrix(Matrix4x4 *mat) const;

public:
	VRFpsCamera();

	void track_vr();
};

}	// namespace goatgfx

#endif	// CAMERA_H_
